package db

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
	"tosServer/internal/pkg/logger"

	"github.com/spf13/viper"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type Database struct {
	Name string
	Host string
	Port string
	User string
	Pass string
}

func init() {
	err := connect()
	if err != nil {
		panic(err)
	}
	go monitor()
}

var (
	DB *gorm.DB

	isConn atomic.Bool

	dbMutex sync.RWMutex
)

func Db() *gorm.DB {
	dbMutex.Lock()
	defer dbMutex.Unlock()

	if !isConn.Load() {
		_ = connect()
	}

	return DB
}

func connect() error {
	dbMutex.Lock()
	defer dbMutex.Unlock()

	if DB != nil {
		return nil
	}

	name := viper.Get("database.name")
	user := viper.Get("database.user")
	pass := viper.Get("database.pass")
	host := viper.Get("database.host")
	port := viper.Get("database.port")

	database := Database{
		Name: name.(string),
		User: user.(string),
		Pass: pass.(string),
		Host: host.(string),
		Port: port.(string),
	}

	db, err := createConnect(database)
	if err == nil {
		for i := 0; i < 3; i++ {
			db, err = createConnect(database)
			if err == nil {
				isConn.Store(true)
				DB = db
				break
			} else {
				logger.Error(err.Error())
				isConn.Store(false)
			}
		}
	}

	return err
}

func createConnect(d Database) (*gorm.DB, error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", d.User, d.Pass, d.Host, d.Port, d.Name)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		return nil, err
	}
	sqlDb, err := db.DB()
	if err != nil {
		return nil, err
	}
	sqlDb.SetMaxIdleConns(100)
	sqlDb.SetMaxOpenConns(100)
	sqlDb.SetConnMaxLifetime(time.Hour)

	return db, err
}

func monitor() {
	for {
		dbMutex.RLock()
		db, err := DB.DB()
		dbMutex.RUnlock()

		if err != nil {
			isConn.Store(false)
			time.Sleep(5 * time.Second)
			_ = connect()
			continue
		}

		err = db.Ping()
		if err != nil {
			isConn.Store(false)
			time.Sleep(5 * time.Second)
			_ = connect()
			continue
		}

		isConn.Store(true)
		time.Sleep(5 * time.Second)
	}

}
